package 泛型;
import java.lang.reflect.*;
//研究泛型与反射之间的关系非常有趣。
//   我们知道，反射和泛型都是Java的一种动态技术。而不像继承和多态，是面向对象的技术。可以这么说，反射和泛型都像是为了弥补像继承和多态这些面向对象技术
//的不足而产生的。模式多是建立在面向对象技术基础上的，因而每种模式多多少少在动态性，或者说扩展性方面有些不足，我们就又结合了反射和泛型对模式进行一定的扩
//展，使它在动态性方面更符合我们的要求。
//   在将这些技术结合起来的过程中，我们多多少少会想到将泛型和反射结合起来。这是非常有趣的话题：范型和反射都使得Java有了一定的扩展性，但同时都有它自己
//的不足，而将这两种技术结合起来，是不是又能解决各自的不足，使得它们在动态性上更上一层楼呢？
//   正像前面所说的，泛型和反射可以相互促进，我们先来看看泛型是怎么帮助反射的。
//   我们知道，使用反射的一个最大的烦恼就是应用反射以后得到的基本上都是Object类型的对象，这种对象要使用，需要我们进行强制类型转化。而我们更知道，泛型
//的功能之一就是消除我们使用强制类型转化。
//1．运行期内初始化对象
//   运行期内初始化对象是我们最常用的反射功能，但是我们通过反射在运行期内得到的对象的类型通常是Object类型的，而这个对象又需要我们在使用的时候进行强制
//类型转化。现在，有了反射，可以使我们不需要做强制类型转化这个工作。
//假设我们已经有了两个类：
class Cls1 {
    public void do1() {
        System.out.println("cls1...");
    }
}

class Cls2 {
    public void do2() {
        System.out.println("cls2...");
    }
}

//我们需要在运行期内初始化这两个对象，我们可以设计如下的初始化方法：
/*
public class 泛型与反射{
	//@SuppressWarnings("unchecked")
	public static <U extends Object> U getInstance(String clsName) throws Exception{
            Class<?> cls = Class.forName(clsName);//?表示使用泛型，T表示声明泛型，T不能出现在泛型表达式中。
            return (U)cls.getDeclaredConstructor().newInstance();//(U) 利用泛型在初始化方法里提前做了强制类型转化
	}
	public static void main(String[] args) throws Exception{
		Cls1 i1 = 泛型与反射.getInstance("泛型.Cls1");
		System.out.println(i1.toString());
		i1.do1();
		
		Cls2 i2 = 泛型与反射.getInstance("泛型.Cls2");
		i2.do2();
		
	}
}
*/

//在这个方法里，我们其实是利用泛型在初始化方法里提前做了强制类型转化的工作，这样使得我们不必在使用的时候进行强制类型转化的工作。
//它们的测试代码如下：
//Cls1 i1 = 泛型与反射.getInstance("泛型.Cls1");
//i1.do1();
//Cls2 i2 = 泛型与反射.getInstance("泛型.Cls2");
//i2.do2()
//测试结果为：
//cls1...
//cls2...
//需要注意的是，使用这种方法有几个问题：
//第一，return (U) cls.newInstance();这个语句会有警告性的错误，好在这种错误不是编译性的错误，还是我们可以承受的。
//第二，编译器不能做类型检查，如Cls1 i1 = Factory.getInstance("泛型.Cls1");这句，
//    换成Cls2 i1 = 泛型与反射.getInstance("泛型.Cls1");也是可以编译过去的。只是在运行的时候才会出错。
// 
//除了上面的方法，还有一种更好的方法。
//这个方法需要我们在运行期内传入的对象是Class对象，当然是经过泛型的Class对象，就像下面的样子：
//Class<Cls1> cls = Cls1.class;
//        try
//        {
//               Intf1 obj = cls.newInstance();
//               obj.do1();
//        }
//        catch(Exception e)
//        {
//               e.printStackTrace();
//        }
//可以很清楚地看到，这里完全没有了强制类型转化，当然也就没有第一种方法所出现的那两个问题。
//运行结果为：
//cls1...
//根据这个方法，我们可以把前面的初始化方法改造为：
//public static <U extends Object> U getInstance(Class<U> cls)
//{
//        try
//        {
//               return cls.newInstance();
//        }
//        catch(Exception e)
//        {
//               e.printStackTrace();
//               return null;
//        }
//}
//我们来进行以下测试：
//Cls1 c1 = Factory.getInstance(Cls1.class);
//        c1.do1();

public class 泛型与反射{
	//@SuppressWarnings("unchecked")
	public  <U extends Object> U getInstance(Class<U> cls) throws Exception{//非静态方法
            return cls.getDeclaredConstructor().newInstance();
	}
	public static <U extends Object> U getInstance1(Class<U> cls) throws Exception{//非静态方法
        return cls.getDeclaredConstructor().newInstance();
}
	public static void main(String[] args) throws Exception{
		Cls1 i1 = new 泛型与反射().getInstance(Cls1.class);
		System.out.println(i1.toString());
		i1.do1();
		
		Cls2 i2 = 泛型与反射.getInstance1(Cls2.class);
		System.out.println(i2.toString());
		i2.do2();
		
	}
}
//测试结果为：
//cls1...
//这时候，如果我们将上面的测试代码改为：
//Cls2 c1 = Factory.getInstance(Cls1.class);
//就会出现编译错误，可以看到，我们的第二种方法的确避免了第一种方法的弱点，但第一种方法的好处是，只需要往初始化方法里输入类名作为参数。
// 
//2．运行期内调用方法
//使用过反射的人都知道，运行期内调用方法后得到的结果也将是一个Object对象。这样的结果在一般的方法反射上也就是可以忍受的了。但是有时候也是不能忍受的，比如，
//我们想反射List<T>类的iterator()方法。一般的，我们使用反射可以这么做：
//try{
//    Class<?> cls = Class.forName("java.util.ArrayList");
//    Method m = cls.getDeclaredMethod("iterator",new Class[0]);
//    return m.invoke(this,new Object[0]);
//}catch(Exception e){
//    e.printStackTrace();
//    return null;
//}
//当然，这里返回的是一个Object对象，而List<T>类的iterator()的实际返回类型为T。很明显，我们可以将上面的代码做如下的修改：
//try{
//    Class<?> cls = Class.forName("java.util.ArrayList");
//    Method m = cls.getDeclaredMethod("iterator",new Class[0]);
//    return (T)m.invoke(this,new Object[0]);
//}catch(Exception e){
//    e.printStackTrace();
//    return null;
//}
//同样，我们的代码也会遇到警告性的错误。但是我们可以置之不理。
// 
//3．运行期内初始化数组对象
//同样，在运行期内初始化得到的数组也是一个Object对象。就像下面的样子：

//Class clazz = Class.forName("java.lang.String");
//创建一个长度为10的字符串数组，在Java中数组也可以作为Object对象
//Object array = Array.newInstance(clazz, 10);

//Object o = Array.newInstance(int.class,10);//创建一个长度为10的int数组
//如果我想在getArray()方法里得到数组对象，就会得到像下面这个样子的代码：
//public Object getArray(Class cls,int size){
//    return Array.newInstance(cls,size);
//}
//这显然不会令我们满意。因为如果我们输入一个Class<T>类型的参数，就希望返回一个T类型的结果。
//在这种想法下，我们就使用泛型将getArray()方法做如下修改：
//public T[] getArray(Class<T> cls,int size){
//    return (T[])Array.newInstance(cls,size);
//}
//这样的修改，将使我们得到一个比较满意的结果。同样，上面的代码也会得到一个警告性的错误。但是，使用泛型使我们的结果得到了很大的优化。
// 
//上面的几个例子让我们看到了泛型能够帮助反射，而且是大大优化了反射的结果。但在同时，反射也不是被动的接受泛型的帮助，反射同样也可以帮助泛型。这是基于反射的基本
//工作原理：得到数据的元数据。也就是说，可以通过反射得到泛型的元数据。除此之外，反射也有利于帮助泛型数据运行期内初始化。
//下面以一两个简单例子加以说明。
// 
//4．使用反射初始化泛型类
//我们通常会在一个类里这样使用泛型：
//public final class Pair<A,B> {
//public final A fst;
//public final B snd;
// 
//public Pair(A fst, B snd) {
//this.fst = fst;
//this.snd = snd;
//}
//……
//}
//这当然是我们最基本的用法，但常常会这样：编译器希望知道更多的关于这个未知对象如A fst的信息，这样，我们可以在运行期内调用某一些方法。大家说啊，这很容易啊，
//我们可以把这种未知类型作为参数输入。呵呵，这就对了，有了这样参数，下一步，我们就要使用反射在运行期内调用它的方法。
//关于这样的例子，我在这里不再给出。我在这里给出一个简单一些的例子，就是对泛型类初始化需要调用的构造器。
//对于上面的Pair<A,B>类，如果构造器的输入参数的类型不是A和B，而是Class<A>和Class<B>，那么我们就不得不在构造器里使用反射了。
//public Pair(Class<A> typeA, Class<B> typeB) {
//this.fst = typeA.newInstance();
//this.snd = typeB.newInstance();
//……
//}
//由此可见，对于泛型里的未知类型参数，我们也完全可以和普通类型一样使用反射工具。即可以通过反射对这些未知类型参数做反射所能做到的任何事情。
// 
//5．  使用反射得到泛型信息
//关于这一个小结的问题，显得更加得有趣。
//我们还是以上面的Pair<A,B>作为例子，假如我们在一个类中使用到了这个类，如下所示：
//public Class PairUser
//{
//        private Pair<String,List> pair;
//        ……
//}
//如果我们对PairUser类应用反射，我们可以很轻松的得到该类的属性pair的一些信息，如它是private还是public的，它的类型等等。
//如果我们通过反射得到pair属性的类型为Pair以后，我们知道该类是一个泛型类，那么我们就想进一步知道该泛型类的更进一步的信息。比如，泛型类的类名，泛型参数的信息等等。
//具体到上面的PairUser类的例子，我现在想知道它的属性pair的一些信息，比如，它的类型名、泛型参数的类型，如String和List等等这些信息。所要做的工作如下：
//首先是取得这个属性
//Field field = PairUser.class.getDeclaredField("pair");
//然后是取得属性的泛型类型
//Type gType = field.getGenericType();
//再判断gType是否为ParameterizedType类型，如果是则转化为ParameterizedType类型的变量
//ParameterizedType pType = (ParameterizedType)gType;
//取得原始类型
//Type rType = pType.getRawType();
//然后就可以通过rType.getClass().getName()获得属性pair的类型名。
//最后获取参数信息
//Type[] tArgs = pType.getActualTypeArguments();
//可以通过tArgs[j].getClass().getName()取得属性pair的泛型参数的类型名。
//完整的代码如下：
//try
//        {
//               Field field = PairUser.class.getDeclaredField("pair");
//               Type gType = field.getGenericType();
//               if(gType instanceof ParameterizedType)
//               {
//                      ParameterizedType pType = (ParameterizedType)gType;
//                      Type rType = pType.getRawType();
//                      System.out.println("rawType is instance of " +
//                                    rType.getClass().getName());
//                      System.out.println(" (" + rType + ")");
//                      Type[] tArgs = pType.getActualTypeArguments();
//                      System.out.println("actual type arguments are:");
//                      for (int j = 0; j < tArgs.length; j++) {
//                             System.out.println(" instance of " +
//                                           tArgs[j].getClass().getName() + ":");
//                             System.out.println(" (" + tArgs[j] + ")");
//                      }
//               }
//               else
//               {
//                      System.out.println("getGenericType is not a ParameterizedType!");
//               }
//        }
//        catch(Exception e)
//        {
//               e.printStackTrace();
//        }
// 
//}
//输出结果为：
//rawType is instance of java.lang.Class
// (class Pair)
//actual type arguments are:
// instance of java.lang.Class:
// (class java.lang.String)
// instance of java.lang.Class:
// (interface java.util.List)